/*
 * @(#) $Header$
 *
 * Copyright (C) 2010  Daniel Léonard
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package ca.forklabs.crypto.md;

/**
 * Class {@code SmallSHA} groups together the SHA digest that use 32-bit
 * registers.
 *
 * @author   <a href="mailto:forklabs at gmail.com?subject=ca.forklabs.crypto.md.SmallSHA">Daniel Léonard</a>
 * @version $Revision$
 */
public abstract class SmallSHA extends SHAFamily {

//----------------------------
// Instance variable
//----------------------------

    /** The H buffer. */
    private int[] H;

    /** The W buffer. */
    private int[] W;


//---------------------------
// Constructor
//---------------------------

   /**
    * Constructor.
    * @param   name   the name of the algorithm
    * @param   buffer_len   the size of one block in bytes.
    * @param   digest_len   the size of the digest in bytes.
    * @param   h_len   the size of the H buffer in blocks of 32 bits.
    * @param   w_len   the size of the W buffer in blocks of 32 bits.
    */
   protected SmallSHA(String name, int buffer_len, int digest_len, int h_len, int w_len) {
      super(name, buffer_len, digest_len);

      int[] h = new int[h_len];
      this.setHBuffer(h);

      int[] w = new int[w_len];
      this.setWBuffer(w);
      }


//---------------------------
// Accessors and mutators
//---------------------------

   /**
    * Completely changes the W buffer.
    * @param   state   the new W buffer.
    */
   protected void setWBuffer(int[] state) {
      this.W = state;
      }

   /**
    * Gets the W buffer.
    * @return   the W buffer.
    */
   protected int[] getWBuffer() {
      return this.W;
      }

   /**
    * Completely changes the H buffer.
    * @param   state   the new H buffer.
    */
   protected void setHBuffer(int[] state) {
      this.H = state;
      }

   /**
    * Gets the H buffer.
    * @return   the H buffer.
    */
   protected int[] getHBuffer() {
      return this.H;
      }


//----------------------------
// Concrete core methods
//----------------------------

   @Override
   protected byte[] coreDoFinal() {
      long count = this.getCount();
      long bits = count * 8L;
      int buf_len = this.getBufferLen();

      int index = (int) (count % buf_len);
      int padlen = (index < (buf_len - 8)) ? (buf_len - 8) - index : (2 * buf_len - 8) - index;
      byte[] last = new byte[padlen + 8];
      last[0] = (byte) 0x80;
      padlen = AbstractBlockMessageDigest.breakLongBE(bits, last, padlen);
      this.engineUpdate(last, 0, padlen);

      int digest_len = this.getDigestLen();
      byte[] digest = new byte[digest_len];
      int[] h = this.getHBuffer();
      for (int i = 0, j = 0; j < digest_len; i++) {
         j = AbstractBlockMessageDigest.breakIntBE(h[i], digest, j);
         }

      this.coreInit();
      return digest;
      }


//---------------------------
// Implemented methods from java.lang.Cloneable
//---------------------------

   /**
    * Makes a deep copy this message digest in its current state.
    * @return   a clone of this message digest instance.
    */
   @Override
   public Object clone() throws CloneNotSupportedException {
      SmallSHA sha = (SmallSHA) super.clone();
      sha.H = this.H.clone();
      sha.W = this.W.clone();
      return sha;
      }

   }

